#pragma once

#include <iostream>
#include <memory>
#include <mutex>
#include <cassert>
#include <unordered_map>
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include "google/protobuf/map.h"

namespace bitmq
{
    // 1. 定义交换机数据类
    struct Exchange
    {
        using ptr = std::shared_ptr<Exchange>;
        
        Exchange()
        {}

        Exchange(const std::string& ename, ExchangeType& etype,
            bool edurable, bool eauto_delete, 
            const google::protobuf::Map<std::string, std::string>& eargs)
                : name(ename)
                , type(etype)
                , durable(edurable)
                , auto_delete(eauto_delete)
                , args(eargs)
        { }

        //解析str_args字符串，将结果存储到成员中（反序列化)
        void setArgs(const std::string& str_args)
        {
            std::vector<std::string> sub_args;
            size_t ret = bitmq::StringHelper::splite(str_args, "&", sub_args);
            for (auto& str : sub_args)
            {
                size_t pos = str.find('=');
                std::string key = str.substr(0, pos);
                std::string value = str.substr(pos+1);
                args.insert({key, value});
            }
        }

        //将args中的内容进行序列化，返回字符串
        std::string getArgs()
        {
            std::string ret;
            for (auto& [k, v] : args)
            {
                ret += k + '=' + v;
                ret += '&';
            }
            if (ret.size() != 0)
                ret.pop_back();
            return ret;
        }

        std::string name;          // 1）交换机名称
        bitmq::ExchangeType type;  // 2）交换机类型
        bool durable;              // 3）是否持久化标志
        bool auto_delete;          // 4）是否自动删除标志
        google::protobuf::Map<std::string, std::string> args; // 5）其他参数
        //args存储键值对，在存储数据库时，会组织成一个格式字符串存储：key=val&key=val
    };

    // 2. 定义交换机数据持久化类（数据持久化的 sqlite3 数据库中）
    class ExchangeMapper
    {
    public:
        ExchangeMapper(const std::string& dbfile)
            : _sql_helper(dbfile)
        {
            //创建目录
            std::string path = bitmq::FileHelper::parentDirectory(dbfile);
            bitmq::FileHelper::createDirectory(path);
            assert(_sql_helper.open());
            createTable();
        }

        void createTable()  // 1）创建交换机数据表
        {
            #define CREATE_TABLE "create table if not exists exchange_table( \
                name varchar(32) primary key, \
                type int, \
                durable int, \
                auto_delete int, \
                args varchar(128));"
            bool ret = _sql_helper.exec(CREATE_TABLE, nullptr, nullptr);
            if (ret == false)
            {
                DLOG("创建交换机数据库表失败!!");
                abort();  //直接异常退出程序
            }
        }

        void removeTable() //  2) 删除交换机数据表
        {
            #define DROP_TABLE "drop table if exists exchange_table;"
            bool ret = _sql_helper.exec(DROP_TABLE, nullptr, nullptr);
            if (ret == false)
            {
                DLOG("删除交换机数据库表失败!!");
                abort();  //直接异常退出程序
            }
        }

        void insert(Exchange::ptr& exchange)  // 3）新增交换机数据
        {
            #define INSERT_SQL "insert into exchange_table values('%s', %d, %d, %d, '%s');"
            char sql_str[4096] = { 0 };
            std::string args_str = exchange->getArgs();
            sprintf(sql_str, INSERT_SQL, exchange->name.c_str(), exchange->type, exchange->durable,
                exchange->auto_delete, args_str.c_str());
            bool ret = _sql_helper.exec(sql_str, nullptr, nullptr);
            if (ret == false)
            {
                DLOG("新增交换机数据失败!!");
            }
        }

        void remove(const std::string& name)  // 4）移除交换机数据
        {
            std::string delete_sql = "delete from exchange_table where name = '";
            delete_sql += name + "';";
            bool ret = _sql_helper.exec(delete_sql, nullptr, nullptr);
            if (ret == false)
            {
                DLOG("删除交换机数据失败!!");
            }
        }
        
        using ExchangeMap = std::unordered_map<std::string, Exchange::ptr>;
        ExchangeMap recovery()  // 5）查询所有交换机数据
        {
            ExchangeMap result;
            std::string sql = "select name, type, durable, auto_delete, args from exchange_table;";
            _sql_helper.exec(sql, selectAllCallback, &result);
            return result;
        }

    private:
        static int selectAllCallback(void* arg, int numCol, char** row, char** fields)
        {
            ExchangeMap* result = (ExchangeMap*)arg;
            auto exp = std::make_shared<Exchange>();
            exp->name = row[0];
            exp->type = (bitmq::ExchangeType)std::stoi(row[1]);
            exp->durable = (bool)std::stoi(row[2]);
            exp->auto_delete = (bool)std::stoi(row[3]);
            if (row[4])
                exp->setArgs(row[4]);
            result->insert({exp->name, exp});
            return 0;
        }

    private:
        bitmq::SqliteHelper _sql_helper;
    };

    // 3. 定义交换机数据管理类
    class ExchangeManager
    {
    public:
        using ptr = std::shared_ptr<ExchangeManager>;
        using ExchangeMap = std::unordered_map<std::string, Exchange::ptr>;

        ExchangeManager(const std::string& dbfile)
            : _mapper(dbfile)
        {
            //恢复所有数据
            _exchanges = _mapper.recovery();
        }

        // 1）声明交换机，并添加管理（存在则 OK，不存在则创建）
        bool declareExchange(const std::string& name, bitmq::ExchangeType type, 
            bool durable, bool auto_delete, const google::protobuf::Map<std::string, std::string>& args)
        {
            //需要持久化存储才会存储到磁盘中
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            //交换机已经存在
            if (it != _exchanges.end())
                return true;
            auto exp = std::make_shared<Exchange>(name, type, durable, auto_delete, args);
            if (durable == true)
            {
                _mapper.insert(exp);
            }
            _exchanges.insert({name, exp});
            return true;
        }

        // 2）删除交换机
        void deleteExchange(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
                return;
            //只有持久化存储的交换机才需要去数据库中删除数据
            if (it->second->durable == true)
                _mapper.remove(name);
            _exchanges.erase(name);
        }

        // 3）获取指定交换机对象
        Exchange::ptr selectExchange(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
                return {};
            return it->second;
        }

        // 3）判断交换机是否存在
        bool exists(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
                return false;
            return true;
        }

        // 4）销毁所有交换机数据
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _exchanges.clear();
        }

        // 5）返回交换机的数量
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _exchanges.size();
        }

    private:
        std::mutex _mutex;
        ExchangeMapper _mapper;
        ExchangeMap _exchanges;
    };
}